﻿using System.Configuration;

namespace MockingBird.Simulator.Framework
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.ServiceModel.Channels;
    using System.Threading;
    using System.Xml;
    using Microsoft.Practices.Unity;
    using MockingBird.Common.Infrastructure.DependencyResolution;
    using MockingBird.Common.Infrastructure.Logging;
    using MockingBird.Simulator.Framework.Configuration;
    using MockingBird.Simulator.Framework.Interfaces;

    public class FrameworkFacade
    {
        public IExecutionContext ExecContext { get; set; }
        public IRequestDistributor Distributor { get; set; }
        public ILogger Logger { get; set; }

        static FrameworkFacade()
        {
            Trace.WriteLine("Framework Facade Static Constructor");
            Trace.WriteLine("Initializing dependencies");
            DependencyResolver.ConfigurationFile = ConfigurationManager.AppSettings["mockingbird_configuration_folder"];
            Trace.WriteLine("Unity config file path : " + DependencyResolver.ConfigurationFile);

            Trace.WriteLine("Configuring default container");
            DependencyResolver.ConfigureDefaultContainer();

            DependencyResolver.DefaultContainer.Resolve<IConfigurationDataProvider>();
        }

        public static Message ProcessRequest(Message message, bool expectResponse)
        {
            Trace.WriteLine(string.Format("FrameworkFacade.ProcessRequest on thread ID: {0}", Thread.CurrentThread.ManagedThreadId));
            FrameworkFacade ff = new FrameworkFacade();
            return ff.InternalProcessRequest(message, expectResponse);
        }

        private FrameworkFacade()
        {
            DependencyResolver.ConfigureDefaultContainer();
            DependencyResolver.ConfigureDefaultContainer("PerRequest");
        }

        private Message InternalProcessRequest(Message inMsg, bool expectResponse)
        {
            try
            {
                IConfigurationDataProvider configurationDataProvider = DependencyResolver.DefaultContainer.Resolve<IConfigurationDataProvider>();
                ApplicationConfiguration appConfig = new ApplicationConfiguration(configurationDataProvider);
                ExecContext = new MockingBird.Simulator.Framework.ExecutionContext(appConfig, inMsg);
                LoggingConfigurationProvider lcp = new LoggingConfigurationProvider();
                lcp.LoggingConfigurationFolder = appConfig.ConfigurationFilesRoot;
                DependencyResolver.DefaultContainer.RegisterInstance<ILoggingConfigurationProvider>(lcp);

                Trace.WriteLine(string.Format("Registering ExecContext on thread ID: {0}", Thread.CurrentThread.ManagedThreadId));
                DependencyResolver.DefaultContainer.RegisterInstance<IExecutionContext>(ExecContext, new PerThreadLifetimeManager());
            }
            catch (Exception exc)
            {
                Trace.WriteLine("exception encountered...");
                LogException(exc);
                throw;
            }

            //delegate processing to the overloaded method
            XmlDocument response = ProcessRequest();
            if (expectResponse && (null == response))
            {
                Trace.WriteLine("Null Response");
            }

            if (expectResponse)
            {
                XmlReader bodyReader = XmlReader.Create(new StringReader(response.OuterXml));
                Message outMsg = Message.CreateMessage(inMsg.Version, "", bodyReader);
                Trace.WriteLine("Created out message. About to return.");
                return outMsg;
            }
            return null;
        }

        /// <summary>
        /// Core method that picks up the request, signals its arrival and then hands it off for processing
        /// Uses the httpContext property
        /// </summary>
        public XmlDocument ProcessRequest()
        {
            //Exit if the context is null
            this.ValidateContext();

            if (Logger == null)
            {
                this.ResolveLogger();
            }
            //get the uri from the incoming request
            string uri = ExecContext.Uri.AbsoluteUri;

            //Call the main method of the logger
            Logger.LogRequestReceived(uri);

            //IHandlerDispenser dispenser = DependencyResolver.Container.Resolve<IHandlerDispenser>();

            //Now call the appropriate handler or call the distributor
            if (Distributor == null)
            {
                this.ResolveDistributor();
                //Distributor = DependencyResolver.DefaultContainer.Resolve<IRequestDistributor>();
            }

            Distributor.ProcessRequest();

            return ExecContext.ResponseMessage;
        }

        private void ResolveDistributor()
        {
            Trace.WriteLine(string.Format("Resolving IRequestDistributor on thread ID: {0}", Thread.CurrentThread.ManagedThreadId));
            IHandlerDispenser dispenser = DependencyResolver.DefaultContainer.Resolve<IHandlerDispenser>();
            this.Distributor = new MockingBird.Simulator.Framework.RequestDistributor(this.ExecContext, this.Logger, dispenser);
        }

        private void ValidateContext()
        {
            if (this.ExecContext == null)
            {
                var msg = "ExecutionContext Is Null";
                var ex = new InvalidOperationException(msg);
                this.Logger.LogError(msg, ex);
                throw ex;
            }
        }

        private void ResolveLogger()
        {
            DependencyResolver.ConfigureDefaultContainer();
            IConfigurationDataProvider configurationDataProvider = DependencyResolver.DefaultContainer.Resolve<IConfigurationDataProvider>();
            ApplicationConfiguration appConfig = new ApplicationConfiguration(configurationDataProvider);

            LoggingConfigurationProvider lcp = new LoggingConfigurationProvider();
            lcp.LoggingConfigurationFolder = appConfig.ConfigurationFilesRoot;
            DependencyResolver.DefaultContainer.RegisterInstance<ILoggingConfigurationProvider>(lcp);

            this.Logger = DependencyResolver.DefaultContainer.Resolve<ILogger>();
        }

        private void LogException(Exception e)
        {
            Logger.LogFatal("FrameworkFacade: An exception occurred in your application", e);
        }
    }
}
